package transfer

import (
	"crypto/ecdsa"
	"errors"
	"fmt"
	"log"
	"math/big"
	"net/http"
	"raymond/api/bnb"
	"raymond/global"
	"raymond/model"
	"raymond/result"
	"raymond/util"
	"strings"

	"github.com/ethereum/go-ethereum/crypto"
	"github.com/labstack/echo/v4"
)

type TokenCollectV1VO struct {
	CryptoCurrency string  `json:"cryptoCurrency" validate:"required,oneof=BCPC_TRC20 BCPC_BEP20"`
	ContractAddr   string  `json:"contractAddr" validate:"required"`
	FromAddress    string  `json:"fromAddress" validate:"required,min=40,max=42"`
	GasPr          string  `json:"gasPr" validate:"required,gt=0"`
	ToAddress      string  `json:"toAddress" validate:"required,min=40,max=42"`
	Amount         float64 `json:"amount" validate:"required,gt=0"`
	Hash           string  `json:"hash,omitempty"`
}

func TokenCollectV1API(c echo.Context) error {
	vo := new(TokenCollectV1VO)
	if err := c.Bind(vo); err != nil {
		return err
	}
	if err := c.Validate(vo); err != nil {
		return err
	}
	var chain global.CHAIN
	if vo.CryptoCurrency == "BCPC_BEP20" {
		chain = global.BNB
	} else if vo.CryptoCurrency == "BCPC_TRC20" {
		chain = global.TRC
	} else {
		return errors.New(result.Message[result.CRYPTO_CURRENCY_ERROR])
	}
	isValidFromAddress := util.IsValidAddress(vo.FromAddress)
	if isValidFromAddress {
		isContract, err := util.IsContractAddress(vo.FromAddress)
		if err != nil {
			return err
		}
		if isContract {
			return errors.New(result.Message[result.IS_CONTRACT_ADDRESS])
		}
	}
	isValidToAddress := util.IsValidAddress(vo.ToAddress)
	if isValidToAddress {
		isContract, err := util.IsContractAddress(vo.ToAddress)
		if err != nil {
			return err
		}
		if isContract {
			return errors.New(result.Message[result.IS_CONTRACT_ADDRESS])
		}
	}
	isValidContractAddr := util.IsValidAddress(vo.ContractAddr)
	if isValidContractAddr {
		isContract, err := util.IsContractAddress(vo.ContractAddr)
		if err != nil {
			return err
		}
		if !isContract {
			return errors.New(result.Message[result.NOT_CONTRACT_ADDRESS])
		}
	}
	bsnWallet, err := model.GetBSNWalletByAddr(chain, vo.FromAddress)
	if err != nil {
		return err
	}
	var txHash string
	if string(chain) == string(global.BNB) {
		collectNumber := bnb.TOKEN.ToWei(vo.Amount)
		txHash, err = BNBCollect(bsnWallet.Priv, bsnWallet.BNBAddr, vo.GasPr, vo.ToAddress, collectNumber, vo.ContractAddr)
		if err != nil {
			return err
		}
	} else if string(chain) == string(global.TRC) {
		txHash, err = TRC20Collect()
		if err != nil {
			return err
		}
	}
	vo.Hash = txHash
	return c.JSON(
		http.StatusOK,
		result.Ok(vo),
	)
}

func BNBCollect(fromKey string, fromAddr string, gasKey string, toAddr string, value *big.Int, contractAddr string) (string, error) {
	if strings.EqualFold(fromAddr, toAddr) {
		return "", errors.New(result.Message[result.SAME_ACCOUNT])
	}
	gasPrKey, err := crypto.HexToECDSA(gasKey)
	if err != nil {
		return "", err
	}
	gasPub := gasPrKey.Public()
	gasEcdsa, ok := gasPub.(*ecdsa.PublicKey)
	if !ok {
		return "", errors.New("cannot assert type: publicKey is not of type *ecdsa.PublicKey")
	}
	gasAddr := crypto.PubkeyToAddress(*gasEcdsa)
	ok, approveTx, transferHash, err := bnb.Approve(fromAddr, fromKey, gasAddr.Hex(), gasKey, value, contractAddr)
	if err != nil {
		if !ok {
			return "", err
		} else {
			// 额度充足,可以直接转账
		}
	} else {
		approveHash := approveTx.Hash()
		log.Printf("approveHash:%s, transferHash: %s \n", approveHash, transferHash)
	}

	tx, err := bnb.TransferFrom(gasKey, fromAddr, toAddr, value, contractAddr)
	if err != nil {
		return "", err
	}

	return tx.Hash().Hex(), nil
}

func TRC20Collect() (string, error) {
	fmt.Println("collect success")
	return "", nil
}
